import { ValidateFunction } from "ajv";
import { IMiddleware } from "koa-router";
import { Model } from "mongoose";

import { makeResponse, HttpStatusCode } from "../util/response";
import { ajv } from "../util/ajv";

// 从 Body 校验
export function validateBody(schemaId: string) {
  const validateFn = ajv.getSchema(schemaId) as ValidateFunction;

  const validatorMiddleware: IMiddleware = async (ctx, next) => {
    validateFn(ctx.request.body);
    if (validateFn.errors) {
      makeResponse(
        ctx,
        HttpStatusCode.BadRequest,
        ajv.errorsText(validateFn.errors, { separator: "\n" })
      );
    } else await next();
  };

  return validatorMiddleware;
}

// 从 query 校验
export function validateQuery(schemaId: string) {
  const validateFn = ajv.getSchema(schemaId) as ValidateFunction;

  const validatorMiddleware: IMiddleware = async (ctx, next) => {
    validateFn(ctx.query);
    if (validateFn.errors) {
      makeResponse(
        ctx,
        HttpStatusCode.BadRequest,
        ajv.errorsText(validateFn.errors, { separator: "\n" })
      );
    } else await next();
  };

  return validatorMiddleware;
}

// 从 params 校验
export function validateParams(schemaId: string) {
  const validateFn = ajv.getSchema(schemaId) as ValidateFunction;

  const validatorMiddleware: IMiddleware = async (ctx, next) => {
    validateFn(ctx.params);
    if (validateFn.errors) {
      makeResponse(
        ctx,
        HttpStatusCode.BadRequest,
        ajv.errorsText(validateFn.errors, { separator: "\n" })
      );
    } else await next();
  };

  return validatorMiddleware;
}

// 检测分页参数有效性
export function validatePage(model: Model<any, any, any>) {
  const returnMiddleware: IMiddleware = async (ctx, next) => {
    const current = ctx.query.current as any as number;
    const limit = ctx.query.limit as any as number;

    const total = await model.countDocuments().exec();
    const maxPage = Math.ceil(total / limit);
    if (current > maxPage && maxPage > 0)
      makeResponse(ctx, HttpStatusCode.BadRequest, "当前页码超出总页数");
    else {
      // 将 total 挂载到 ctx.state 后面中间件使用
      ctx.state.total = total;
      await next();
    }
  };
  return returnMiddleware;
}
